/** The AccelerometerPrediction class.
 * 
 * @author Sauvik Das
 */

package com.type.TypingDetector;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Scanner;

import android.app.Activity;
import android.app.ListActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.AdapterView.OnItemClickListener;

public class AcceleromterUpdated extends ListActivity {
	private SensorManager sm;
    private List<Sensor> sensors;
    private Sensor accel; //Primary Accelerometer
    boolean accelerometerAvailable = false; //If an accelerometer is available or not
    TextView accelText; //TextView for debugging
    TextView diffText; //TextView for debugging
    ToggleButton enabled; //Whether prediction is enabled or not
    boolean active=false, first=true; //
    float[] oldVals; //Previous values of the accelerometer. Deprecated.
    int counter; //current counter of the dataArr
    double[] dataArr; //currently stored Values of the accelerometer readings
    double[][][] data;
    
    
    //private ArrayList<PredictionType> predTypes; //Will be used to store prediction types when changed
    
    //Different types of prediction supported by this Activity. Deprecated.
    enum PREDICTION_TYPE {PHONE_ALONE, SITTING, WALKING, RUNNING, JUMPING;
    	public static PREDICTION_TYPE getType(int i) {
    		if (i==0) return PHONE_ALONE;
    		else if (i==1) return SITTING;
    		else if (i==2) return WALKING;
    		else if (i==3) return RUNNING;
    		else if (i==4) return JUMPING;
    		else return null;
    	}
    	
    	public static String toString(PREDICTION_TYPE type) {
    		switch (type) {
    		case PHONE_ALONE: 
    			return "Phone Detached";
    		case SITTING:
    			return "Idle Sitting/Standing";
    		case WALKING:
    			return "Walking";
    		case RUNNING:
    			return "Running";
    		case JUMPING:
    			return "Jumping";
    		default:
    			return "";
    		}
    	}
    }
    
    private final int indSize = 5; //Number of types of prediction
    private ArrayList<LogElement> logs; //Predicted Activities
    int watchCounter; //Used if a change in activity is suspected
    final int dataArrSize=1024; //Size of dataArr
    final int fiveSeconds=256; //Number of values to look at when comparing data, approximately 8 seconds worth
    boolean currentlyWatching=false; //Currently suspicious of activity change or not
    private long currentTimer; //Time the current activity began
    private long altTimer; //Time the suspected changed activity began
    private Comparison[] staticVals; //Values of the comparison data given by the files of sitting/running etc. Dom. Frequency and Average Acceleration
    private Calendar c;
    FourierTransform ft;
    private int startCounter; //The index in dataArray that the current activity started out as
    private int altStartCounter; //The index in dataArray that the suspected new activity started out as
    private OrderAdapter mAdapter; //The adapter for the display of the list data
    private boolean interpreting=false;
    private long globalStartTime; //the initial start time of the application
    private int cooldown; //Number of change events to wait for before trying to reinterpret data
    private final int SMOOTH_ITERATIONS=3;
    private boolean restart=false;
    private final String PATH_TO_FILE="data/data/com.accel.accelerometer";
    private final String FOLDER_NAME="SavedFiles";
    private final int LAST_HARDCODED_INDEX=6;
    
  //Database stuff
    protected final String DATABASE_TABLE="AccelVals2";
    protected final String FN_TABLE="filenames3";
    protected ArrayList<String> colNames= new ArrayList<String>(Arrays.asList("ID", "Title", "Dominant", "Average", "MaxAmp", "MinAmp"));
    protected ArrayList<String> colOpts= new ArrayList<String>(Arrays.asList("integer","text","real","real","real","real"));
    protected ArrayList<String> rowNames= new ArrayList<String>(Arrays.asList("Phone Detached", "Idle", "Walking", "Running", "Jumping", "Climbing Stairs", "Descending Stairs"));
    protected ArrayList<String> fileNames;//= new ArrayList<String>(Arrays.asList("tabletopnew.txt", "sittingnew.txt", "walkingnewb.txt", "runningnewb.txt", "jumpingnewb.txt", "stairsup.txt", "stairsdown.txt"));
    protected ArrayList<String> fntbColNames = new ArrayList<String>(Arrays.asList("ID","Type","Filename"));
    protected ArrayList<String> fntbColOpts = new ArrayList<String>(Arrays.asList("integer","text","text"));
    private DatabaseQuery query, fnQuery;
    
    
    /** This method creates the layout of the Activity and initializes most global variables and sensors.
     * If onRestoreInstanceState was called before this method (meaning this is not the first time the Activity has been
     * opened, then the variable initializations are bypassed, and only the sensors are initialized.
     */
    public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	setContentView(R.layout.main);
    	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    	
    	final DataObject data = (DataObject) getLastNonConfigurationInstance();
    	accelText=(TextView) findViewById(R.id.accelText);
		accelerometerAvailable = instantiateSensors();
		//diffText=(TextView) findViewById(R.id.diffText);
		enabled = (ToggleButton)findViewById(R.id.togglebutton);
		c = Calendar.getInstance();
		c.setTimeInMillis(0);
		if (query==null) query=new DatabaseQuery(this, colNames, colOpts, DATABASE_TABLE);
		if (fnQuery==null) fnQuery=new DatabaseQuery(this, fntbColNames, fntbColOpts, FN_TABLE);
		ArrayList<String> fns=fnQuery.getDataNS(new String[] {"Filename"}, null, null, null, null);
		ArrayList<String> dbVals=query.getDataNS(new String[] {"Title", "Dominant", "Average", "MaxAmp", "MinAmp"}, null, null, null, null);
		AssetManager am = getAssets();
		
		if (data==null) {
			dataArr = new double[dataArrSize];
			logs=new ArrayList<LogElement>();
			//logs.add(new LogElement("Running", 0, PREDICTION_TYPE.RUNNING));
			//staticVals = new Comparison[indSize];
			staticVals = new Comparison[dbVals.size()/5];
			counter = 0;
			
			/*try {
				String[] fileNames = am.list("");
				//diffText.setText(fileNames[1]+fileNames[2]);
				
				staticVals[0] = parseFiles(am.open("tabletopnew.txt", AssetManager.ACCESS_STREAMING),0);
				staticVals[1] = parseFiles(am.open("sittingnew.txt", AssetManager.ACCESS_STREAMING),1);
				staticVals[2] = parseFiles(am.open("walkingnewb.txt", AssetManager.ACCESS_STREAMING),2);
				staticVals[3] = parseFiles(am.open("runningnewb.txt", AssetManager.ACCESS_STREAMING),3);
				staticVals[4] = parseFiles(am.open("jumpingnewb.txt", AssetManager.ACCESS_STREAMING),4);
				//staticVals = parseFiles(fileNames);
			}
			catch (FileNotFoundException e) {
				diffText.setText("File not Found");
			}
			catch (IOException e) {
				diffText.setText("meh");
			}*/
			
			for (int i=0; i<dbVals.size(); i+=5) {
			  Comparison c = new Comparison(dbVals.get(i), Double.valueOf(dbVals.get(i+1)), Double.valueOf(dbVals.get(i+2)), Double.valueOf(dbVals.get(i+3)), Double.valueOf(dbVals.get(i+4)));
			  staticVals[i/5]=c;
			}
			
			//accelText.setText("" + staticVals[1].getName() + ":" + staticVals[1].getFreq() + "," + staticVals[1].getAvg());
		}
		else
		{
			dataArr=data.dataArr;
			logs=data.logs;
			staticVals=data.staticVals;
			counter=data.counter;
			currentlyWatching=data.currentlyWatching;
			watchCounter=data.watchCounter;
			currentTimer=data.currentTimer;
			altTimer=data.altTimer;
			startCounter=data.startCounter;
			altStartCounter=data.altStartCounter;
			active=data.active;
		}
		
		mAdapter = new OrderAdapter(this, R.layout.list_item, logs);
		setListAdapter(mAdapter);

		ListView lv = getListView();
		lv.setTextFilterEnabled(true);
		
		lv.setOnItemClickListener(new OnItemClickListener() {
		    public void onItemClick(AdapterView<?> parent, View view,
		        int position, long id) {
		      // When clicked, show a toast with the TextView text
		      Toast.makeText(getApplicationContext(), "" + SystemClock.uptimeMillis(),
		          Toast.LENGTH_SHORT).show();
		    }
		  });

		
		//am.close();
		
		enabled.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				active=enabled.isChecked();
				if (!active) {
					restart=true;
					if (logs.size() >0) {
						logs.get(logs.size()-1).setEndTime(SystemClock.uptimeMillis());
					}
				}
			}
        });
		
		
		
		if (accelerometerAvailable)
        {
        	sm.registerListener(mySEL, accel, SensorManager.SENSOR_DELAY_GAME);
        }
		globalStartTime=SystemClock.uptimeMillis();
		cooldown=0;
		
		/*try {
			Scanner scan=new Scanner(am.open(fns.get(7)));
			scan.nextLine();
			while (scan.hasNextLine()) {
				accelText.setText(""+Double.valueOf(scan.nextLine()));
			}
			scan.close();
		}
		catch (Exception e) {
			accelText.setText("Fail");
		}*/
		//accelText.setText("[0.0]");
		//accelText.setText("" + PREDICTION_TYPE.toString(staticVals[1].getType()) + ":" + staticVals[1].getFreq() + "," + staticVals[1].getAvg());
				/*"Sitting:" + staticVals[1].getFreq() + "," + staticVals[1].getAvg() + "|" +
				"Walking:" + staticVals[2].getFreq() + "," + staticVals[2].getAvg() + "|" +
				"Running:" + staticVals[3].getFreq() + "," + staticVals[3].getAvg() + "|" +
				"Jumping:" + staticVals[5].getFreq() + "," + staticVals[5].getAvg() + "|"*/
        
    }
    
    /** Initiates the SensorManager and Accelerometer.
	 * 
	 * @return - true if the initialization was a success, false otherwise
	 */
    private boolean instantiateSensors() {
		// TODO Auto-generated method stub
		sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        sensors = sm.getSensorList(Sensor.TYPE_ACCELEROMETER);
        
        if (sensors.size() > 0) {
        	accel = sensors.get(0);
        	return true;}
        return false;
    }
    
    /** The SensorEventListener for the accelerometer. Called whenever the sensor values are changed.
	 * 
	 */
    private final SensorEventListener mySEL = new SensorEventListener() {
		
    	/** Called whenever Sensor readings change. Converts the readings into one value via Pythagorean Theorem,
    	 * and then stores the combined value in dataArr. From there, follows an algorithm to make predictions.
    	 * 
    	 */
    	public void onSensorChanged(SensorEvent event) {
			double val = Math.sqrt(Math.pow(event.values[0],2) + Math.pow(event.values[1],2) + Math.pow(event.values[2],2));
			dataArr[counter] = val;
			
			if (interpreting && (cooldown==127)) {
				interpreting=false;
			}
			
			if (active) {
				//interpret data
				//is data currently logging? if Log is empty, watch for 5 seconds then predict
				//if Log not empty, is differences[0-2] significantly different than rest of dataArr?
				//if so, record current counter position, watch for 5 seconds: if still different, change activity
				//if not, append time to current activity
				
				if (logs.size()==0 || restart) {
					if (!currentlyWatching) {
						watchCounter = (counter+fiveSeconds) % dataArrSize;
						startCounter=counter;
						currentlyWatching=true;
						currentTimer=SystemClock.uptimeMillis();
					}
					else
					{
						if (counter==watchCounter) {
							//predict
							//add to log
							double[] temp;
							int diff = watchCounter-startCounter;
							if (diff>0) {temp=new double[diff];} //if normal
							else {temp=new double[diff+dataArrSize];} //if wrap around
							for (int j=0; j<temp.length; j++) {
								temp[j]=dataArr[(startCounter+j)%dataArrSize];
							}
							//PREDICTION_TYPE tpe=makePrediction(temp);
							String tpe=makePredictionSt(temp);
							//logs.add(new LogElement(PREDICTION_TYPE.toString(tpe), (SystemClock.uptimeMillis()-currentTimer)/1000, tpe));
							logs.add(new LogElement(tpe, (SystemClock.uptimeMillis()-currentTimer)/1000));
							logs.get(logs.size()-1).setStartTime(currentTimer);
							mAdapter.notifyDataSetChanged();
							//logs.add(new LogElement(PREDICTION_TYPE.RUNNING.toString(), (c.get(Calendar.MILLISECOND)-currentTimer)/1000, PREDICTION_TYPE.RUNNING));
							watchCounter=-1;
							currentlyWatching=false;
							restart=false;
						}
					}
				}
				else {
					//is current differences array prediction different than most recent log prediction?
					if (!currentlyWatching) {
						double[] temp = new double[256];
						for (int j=0; j<temp.length; j++) {
							int index = counter-temp.length+j;
							if (index<0) index+=dataArrSize;
							temp[j]=dataArr[index]; //last 16 data entries
						}
						//PREDICTION_TYPE tpe=makePrediction(temp);
						String tpe=makePredictionSt(temp);
						if (!(tpe.equals(logs.get(logs.size()-1).getName()))) {//placeholder, true should be if current pred and recent are different
							watchCounter = (counter+fiveSeconds) % dataArrSize;
							currentlyWatching=true;
							altStartCounter=counter;
							altTimer=SystemClock.uptimeMillis();
						}
						else { //if data has not changed
						//append time to current log element
							logs.get(logs.size()-1).setDuration((SystemClock.uptimeMillis()-currentTimer)/1000);
						}
					}
					else { //if currentlyWatching
						if (counter==watchCounter) {
							//predict based on the previous 5 seconds
							double[] temp;
							int diff = watchCounter-altStartCounter;
							if (diff>0) {temp=new double[diff];} //if normal
							else {temp=new double[diff+dataArrSize];} //if wrap around
							for (int j=0; j<temp.length; j++) {
								temp[j]=dataArr[(altStartCounter+j)%dataArrSize];
							}
							//PREDICTION_TYPE tpe=makePrediction(temp);
							String tpe=makePredictionSt(temp);
							//if same as before, append time; else new Log Element
							//if not, start new currentTimer=altTimer, and add new log element
							if (tpe.equals(logs.get(logs.size()-1).getName())) {
								logs.get(logs.size()-1).setDuration((SystemClock.uptimeMillis()-currentTimer)/1000);
								//mAdapter.refreshLastView();
							}
							else {
								logs.get(logs.size()-1).setDuration((altTimer-currentTimer)/1000);
								logs.get(logs.size()-1).setEndTime(altTimer);
								//mAdapter.refreshLastView();
								currentTimer=altTimer;
								startCounter=altStartCounter;
								//logs.add(new LogElement(PREDICTION_TYPE.toString(tpe), (SystemClock.uptimeMillis()-altTimer)/1000, tpe));
								logs.add(new LogElement(tpe, (SystemClock.uptimeMillis()-altTimer)/1000));
								logs.get(logs.size()-1).setStartTime(currentTimer);
								interpreting=true;
								cooldown=0;
								mAdapter.notifyDataSetChanged();
							}
							currentlyWatching=false;
							watchCounter=-1;
							//altTimer=0;
							altStartCounter=-1;
						}
					}
				}
				mAdapter.notifyDataSetChanged();
			}
			counter++;
			cooldown++;
			cooldown%=128;
			if (counter>=dataArrSize) {
				counter=0;
			}
			//displayAccelValues(val);
		}
		
		public void onAccuracyChanged(Sensor sensor, int accuracy) {
			
		}
	};
	
	public void displayAccelValues(double val) {
		DecimalFormat df = new DecimalFormat("##0.00000");
		//accelText.setText("[" + df.format(val) + "]");
	}
	
	/** This method is deprecated.
	 * 
	 * @param fileNames
	 * @return
	 * @throws FileNotFoundException
	 */
	private Comparison[] parseFiles(String[] fileNames) throws FileNotFoundException {
		Scanner scan;
		Comparison[] retArr=new Comparison[fileNames.length];
		
		for (int i=0; i < fileNames.length; i++) {
			ArrayList<Double> data=new ArrayList<Double>(); //don't know size, so ArrayList
			scan = new Scanner(new File(fileNames[i]));
			scan.useDelimiter(",");
			scan.nextLine(); //throw out first line
			while (scan.hasNextLine()) { //must be formatted as xval, yval, zval
				String temp=scan.nextLine();
				if (temp!="") {
					double storVal = Double.valueOf(scan.nextLine());
					data.add(storVal);
				}
			}
			
			double[] datR = new double[FourierTransform.nextPowerOfTwo(data.size())];
			double[] datI = new double[datR.length];
			double cTot=0.0;
			for (int j=0; j<datR.length; j++) {
			 	if (j<data.size()) {
					datR[j] = (double)data.get(j);
					cTot+=datR[j];
				}
				else {
					datR[j]=0.0;
				}
				datI[j]=0.0;
			}
			
			double avg = cTot/(data.size());
			FastFourierTransform.fastFT(datR, datI, true);
			double fFreq = findFundamentalFreq(datR, datI);
			retArr[i] = new Comparison(PREDICTION_TYPE.getType(i), fFreq, avg);
			scan.close();
		}
		
		return retArr;
	}
	
	/** This method parses files to initialize the staticVals array with the data accrued from LearningMode.
	 * 
	 * @param strm The Input Stream of the files to be read
	 * @param index The PREDICTION_TYPE index indicating the type of data the current Stream is referring too
	 * @return A comparison object with the average and fundamental frequency
	 * @throws FileNotFoundException
	 */
	private Comparison parseFiles(InputStream strm, int index) throws FileNotFoundException {
		Scanner scan=new Scanner(strm);
		ArrayList<Double> data=new ArrayList<Double>(); //don't know size, so ArrayList
		scan.useDelimiter(",");
		scan.nextLine(); //throw out first line
		while (scan.hasNextLine()) { //must be formatted as xval, yval, zval
			String temp=scan.nextLine();
			if (temp!="") {
				double storVal = Double.valueOf(temp);
				data.add(storVal);
			}
		}
		
		double[] dat=new double[data.size()];
		for (int j=0; j<data.size(); j++) {
			dat[j]=data.get(j).doubleValue();
		}
		
		for (int j=0; j<SMOOTH_ITERATIONS; j++) {
			dat=AcceleromterUpdated.smoothValues(dat);
		}
		
		double[] datR = new double[FourierTransform.nextPowerOfTwo(data.size())];
		double[] datI = new double[datR.length];
		double datMax=0;
		double datMin=Double.MAX_VALUE;
		double cTot=0.0;
		
		for (int j=0; j<datR.length; j++) {
		 	if (j<dat.length) {
		 		if (dat[j]>datMax) {
		 			datMax=dat[j];
		 		}
		 		if (dat[j]<datMin) {
		 			datMin=dat[j];
		 		}
		 		datR[j] = dat[j];
				cTot+=datR[j];
			}
			else { //pad with 0s
				datR[j]=0.0;
			}
			datI[j]=0.0;
		}
		
		double avg = cTot/(data.size());
		FastFourierTransform.fastFT(datR, datI, true);
		double fFreq = findAvgFundamentalFreq(datR, datI);
		scan.close();
		
		return new Comparison(PREDICTION_TYPE.getType(index), fFreq, avg, datMax, datMin);
	}
	
	/** Finds the fundamental frequency of the Fourier Transformed data passed in.
	 * 
	 * @param real The real part of the data
	 * @param imag The imaginary part of the data
	 * @return the fundamental frequency of the data
	 */
	public double findFundamentalFreq(double[] real, double[] imag) {
		double currMax = 0;
		double retVal=-1;
		for (int i=0; i <real.length; i++) {
			double mag = Math.sqrt(Math.pow(real[i],2)+Math.pow(imag[i],2));
			if (mag > currMax) retVal=i;
		}
		return retVal;
	}
	
	public double findAvgFundamentalFreq(double[] real, double[] imag) {
		double retVal=-1;
		double[] maxes=new double[3];
		double minMax=0;
		int maxCounter=0;
		
		for (int i=0; i <real.length; i++) {
			double mag = Math.sqrt(Math.pow(real[i],2)+Math.pow(imag[i],2));
			if (mag > minMax) {
				if (maxCounter<2) {
					maxes[maxCounter] = mag;
					maxCounter++;
				}
				else {
					//find lowest of the current maxes
					double min=Double.MAX_VALUE;
					int indexOfLowest=0;
					for (int j=0; j<maxes.length;j++) {
						if (maxes[j]<min) {min=maxes[j];}
						indexOfLowest=j;
					}
					maxes[indexOfLowest]=mag;
					minMax=mag;
				}
			}		
		}
		return (double)((maxes[0]+maxes[1]+maxes[2])/3);
	}
	
	/** Makes a prediction of the activity represented by the accelerometer values passed in.
	 * Performs a FastFourierTransformation and compares resultant data to other values.
	 *  
	 * @param data the accelerometer values from which to make a prediction
	 * @return the PREDICTION_TYPE based on the values passed in
	 */
	private PREDICTION_TYPE makePrediction(double[] data) {
		for (int i=0; i<SMOOTH_ITERATIONS;i++) {
			data=AcceleromterUpdated.smoothValues(data);
		}
		
		double[] datR = new double[FourierTransform.nextPowerOfTwo(data.length)];
		double[] datI = new double[datR.length];
		double datMax=0;
		double datMin=Double.MAX_VALUE;
		
		double cTot=0.0;
		for (int j=0; j<datR.length; j++) {
		 	if (j<data.length) {
		 		if (data[j]>datMax) {
		 			datMax=data[j];
		 		}
		 		if (data[j]<datMin) {
		 			datMin=data[j];
		 		}
				datR[j] = (double)data[j];
				cTot+=datR[j];
			}
			else { //pad with 0s
				datR[j]=0.0;
			}
			datI[j]=0.0;
		}
		
		double avg = cTot/(data.length);
		FastFourierTransform.fastFT(datR, datI, true);
		double fFreq = findAvgFundamentalFreq(datR, datI);
		Comparison cp = new Comparison("", fFreq, avg, datMax, datMin);
		return findClosestMatch(cp, staticVals);
	}
	
	private String makePredictionSt(double[] data) {
		for (int i=0; i<SMOOTH_ITERATIONS;i++) {
			data=AcceleromterUpdated.smoothValues(data);
		}
		
		double[] datR = new double[FourierTransform.nextPowerOfTwo(data.length)];
		double[] datI = new double[datR.length];
		double datMax=0;
		double datMin=Double.MAX_VALUE;
		
		double cTot=0.0;
		for (int j=0; j<datR.length; j++) {
		 	if (j<data.length) {
		 		if (data[j]>datMax) {
		 			datMax=data[j];
		 		}
		 		if (data[j]<datMin) {
		 			datMin=data[j];
		 		}
				datR[j] = (double)data[j];
				cTot+=datR[j];
			}
			else { //pad with 0s
				datR[j]=0.0;
			}
			datI[j]=0.0;
		}
		
		double avg = cTot/(data.length);
		FastFourierTransform.fastFT(datR, datI, true);
		double fFreq = findAvgFundamentalFreq(datR, datI);
		Comparison cp = new Comparison("", fFreq, avg, datMax, datMin);
		return findClosestMatchSt(cp, staticVals);
	}
	
	/** Finds closest match between cp and given values by comparing the Pythagorean distance of the dominant frequency and average of the data
	 *  
	 *  @param cp the data which needs to be matched
	 *  @param vals the array of data which cp can be matched to
	 *  @return the closest PREDICTION_TYPE to cp*/
	private PREDICTION_TYPE findClosestMatch(Comparison cp, Comparison[] vals) {
		PREDICTION_TYPE retVal = null;
		double bestMatch=Double.MAX_VALUE;
		for (int i=0; i<vals.length; i++) {
			double currMatch=Math.sqrt(Math.pow(cp.getFreq()-vals[i].getFreq(), 2)+Math.pow(cp.getAvg()-vals[i].getAvg(),2)); //pythagorean distance
			if (currMatch < bestMatch) {
				bestMatch=currMatch;
				retVal=vals[i].getType();
			}
		}
		return retVal;
	}
	
	private String findClosestMatchSt(Comparison cp, Comparison[] vals) {
		String retVal = null;
		double bestMatch=Double.MAX_VALUE;
		for (int i=0; i<vals.length; i++) {
			double currMatch=Math.sqrt(Math.pow(cp.getFreq()-vals[i].getFreq(), 2)+Math.pow(cp.getAvg()-vals[i].getAvg(),2)+Math.pow(cp.getMaxAmplitude()-vals[i].getMaxAmplitude(),2)
					+Math.pow(cp.getMinAmplitude()-vals[i].getMinAmplitude(),2)); //pythagorean distance
			if (currMatch < bestMatch) {
				bestMatch=currMatch;
				retVal=vals[i].getName();
			}
		}
		return retVal;
	}
	
	/** The adapter given to display the List of predicted activities
	 * 
	 * @author Sauvik Das
	 *
	 */
	private class OrderAdapter extends ArrayAdapter<LogElement> {

	    private ArrayList<LogElement> items;

	    public OrderAdapter(Context context, int textViewResourceId, ArrayList<LogElement> items) {
	            super(context, textViewResourceId, items);
	            this.items = items;
	    }
	    
	    @Override
	    public View getView(int position, View convertView, ViewGroup parent) {
	            View v = convertView;
	            if (v == null) {
	                LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	                v = vi.inflate(R.layout.list_item, null);
	            }
	            LogElement o = items.get(position);
	            if (o != null) {
	                    TextView tt = (TextView) v.findViewById(R.id.toptext);
	                    TextView bt = (TextView) v.findViewById(R.id.bottomtext);
	                    if (tt != null) {
	                          tt.setText("Activity: "+o.getName());}
	                    if(bt != null){
	                          bt.setText("Time: "+ o.calcDuration(SystemClock.uptimeMillis()) + " seconds");
	                    }
	            }
	            return v;
	    }
	}
	
	/** Saves data which needs to be retained when application is destroyed. Differs from onSaveInstanceState
	 * and onRestoreInstanceState() in that this data does not need to be Serializable, and is simply stored as 
	 * an instance of Object. Requires self-wrote class to save the data
	 * 
	 */
	@Override
	public Object onRetainNonConfigurationInstance() {
	    final DataObject data = new DataObject(logs, staticVals, dataArr, counter, watchCounter, currentlyWatching, currentTimer, altTimer, startCounter, altStartCounter, active);
	    return data;
	}
	
	/** Smooths values of passed in array by averaging with neighbors.
	 * 
	 * @param data the array of data passed in
	 * @return smoothed array
	 */
	public static double[] smoothValues(double[] data) {
		double[] retVal=new double[data.length];
		for (int i=0; i<data.length; i++) {
			double newVal=0;
			if (i==0) {
				newVal=(data[i]+data[i+1])/2;
			}
			else if (i==(data.length-1)) {
				newVal=(data[i-1]+data[i])/2;
			}
			else {
				newVal=(data[i-1]+data[i]+data[i+1])/3;
			}
			retVal[i]=newVal;
		}
		
		return retVal;
	}
	
	
}